package client

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"sync"
	"time"
)

// todo
//  - 支持插件(如接口请求认证等)

const (
	METHOD_GET  = "GET"
	METHOD_POST = "POST"

	// 默认配置
	DefaultUserAgent       = "HttpzClient/1.1"
	DefaultTimeout         = 30 * time.Second // 请求超时(包含拨号/数据传输等时间)
	DefaultDialTimeout     = 2 * time.Second  // 拨号连接超时
	DefaultKeepAlive       = 30 * time.Second // 保持活动连接时间
	DefaultMaxIdleConns    = 100              // 最大空闲连接数
	DefaultMaxConnsPerHost = 10               // 每个主机的最大并发连接数
)

type Client struct {
	httpClient *http.Client
	userAgent  string
	debug      bool
	transport  *http.Transport
	dialer     *net.Dialer
	mu         sync.Mutex // 保护transport修改
}

// 创建默认客户端请求
func Default() *Client {
	c := &Client{
		userAgent: DefaultUserAgent,
	}

	c.initTransport()

	c.httpClient = &http.Client{
		Timeout:   DefaultTimeout,
		Transport: c.transport,
	}

	return c
}

// 初始化连接配置
func (c *Client) initTransport() {
	// 拨号配置
	c.dialer = &net.Dialer{
		Timeout:   DefaultDialTimeout, // 连接超时
		KeepAlive: DefaultKeepAlive,   // 保持活动连接时间
		DualStack: true,               // 启用双栈支持（IPv4/IPv6）
	}

	c.transport = &http.Transport{
		DialContext:         c.dialer.DialContext,   // 连接设置
		MaxIdleConns:        DefaultMaxIdleConns,    // 最大空闲连接数
		MaxConnsPerHost:     DefaultMaxConnsPerHost, // 每个主机的最大并发连接数
		IdleConnTimeout:     90 * time.Second,       // 空闲连接超时时间
		TLSHandshakeTimeout: 10 * time.Second,       // TLS握手超时
	}
}

// 请求超时配置(包含全部), 默认30s, 涉及到文件上传/下载时建议调整到合理的时间
func (c *Client) TimeOut(timeout time.Duration) *Client {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.httpClient.Timeout = timeout
	return c
}

// 最大空闲连接数
func (c *Client) MaxIdleConns(maxIdleConns int) *Client {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.transport.MaxIdleConns = maxIdleConns
	return c
}

// 连接超时配置
func (c *Client) DialTimeout(timeout time.Duration) *Client {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.dialer.Timeout = timeout
	return c
}

// 开启Debug模式
func (c *Client) Debug() *Client {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.debug = true
	return c
}

// 执行HTTP请求
func (c *Client) Do(r *Request, fn func(*Response)) (e error) {
	for i := range 3 {
		e = c.realDo(r, fn)
		if e == nil {
			break
		}

		// 延时
		time.Sleep(time.Duration((1<<i)*1000) * time.Millisecond)
	}

	// 返回最后一次错误
	return e
}

// 真实处理请求
func (c *Client) realDo(r *Request, fn func(*Response)) error {
	// 创建请求信息
	request, e := http.NewRequest(r.method, r.url, nil)
	if e != nil {
		return e
	}

	// 设置请求头
	c.addRequestHeaders(request, r.Header)

	// 调试输出
	c.debugPrintfRequest(request)

	// 处理请求
	resp, e := c.httpClient.Do(request)
	if e != nil {
		c.debugPrintfError(e)
		return e
	}
	defer resp.Body.Close()

	// 调试输出
	c.debugPrintfResponse(resp)

	// 处理数据
	fn(&Response{Response: resp})

	return nil
}

// 添加请求头
func (c *Client) addRequestHeaders(r *http.Request, h *Header) {
	r.Header.Add("User-Agent", DefaultUserAgent)
	for k, v := range h.All() {
		r.Header.Add(k, v)
	}
}

// 调试输出请求
func (c *Client) debugPrintfRequest(r *http.Request) {
	if c.debug {
		requestDump, e := httputil.DumpRequestOut(r, true)
		if e == nil {
			fmt.Println(string(requestDump))
		}
	}
}

// 调试输出响应
func (c *Client) debugPrintfResponse(resp *http.Response) {
	if c.debug {
		responseDump, e := httputil.DumpResponse(resp, true)
		if e == nil {
			fmt.Println(string(responseDump))
		}
	}

}

// 调试输出错误
func (c *Client) debugPrintfError(e error) {
	if c.debug {
		fmt.Println(e.Error())
	}
}
